/***
 * Excerpted from "Programming Concurrency on the JVM",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/vspcon for more book information.
 ***/

import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class Main {
	// Number of days (seconds) the simulation should run for
	private static final int SIM_TIME = 15;
	
	// Vending machine reference
	private static final VendingMachine vendingMachine = VendingMachine
			.create();

	/**
	 * Creates 3 users of the vending machine and schedules them to interact
	 * with the vending machine at specified intervals.
	 */
	public static void main(final String[] args) throws InterruptedException,
	ExecutionException {
		System.out.println("Candy at start: "
				+ vendingMachine.getCandyBarsAvailable());

		System.out.println("Cookies at start: "
				+ vendingMachine.getCookiesAvailable());

		List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();

		for (int i = 0; i < SIM_TIME; i ++){
			
			// Cookie Monster
			tasks.add(new Callable<Object>() {
				public Object call() {

					for (int i = 0; i < 2; i++) {
						if (vendingMachine.getCookie(1)) {
							System.out.println("    Me love cookies");
						} else {
							System.out.println("    Me hungy");
						}

						try {
							Thread.sleep(500);
						} catch (Exception e) {
						}
					}

					return null;
				}
			});

			// Willy Wonka
			tasks.add(new Callable<Object>() {
				public Object call() {

					long request = (long) (1000 * Math.random());

					try {
						Thread.sleep(request);
					} catch (Exception e) {
					}

					if (vendingMachine.getCandyBars(1L)) {
						System.out.println("        The Candy Man Can");
					} else {
						System.out.println("        Violet - you're turning violet");
					}

					try {
						Thread.sleep(1000 - request);
					} catch (Exception e) {
					}

					return null;
				}
			});

			// Fat Albert
			tasks.add(new Callable<Object>() {
				public Object call() {
					// 2-4 attempts to get food
					int foodRuns = (int) (2 + 2 * Math.random());

					// Day left to go through, starts with full day remaining
					long timeAvailable = 1000;

					for (int i = 0; i < foodRuns; i++) {
						// Random interval
						int oneRequest = (int) ((1000 / foodRuns) * Math.random());

						timeAvailable -= oneRequest;

						try {
							Thread.sleep(oneRequest);
						} catch (InterruptedException e) {
						}

						if (vendingMachine.getBoth(1, 1)) {
							System.out.println("            Hey, hey, hey!");
						} else if (vendingMachine.getCandyBars(1)) {
							System.out.println("            At least I got a candy bar.");
						} else if (vendingMachine.getCookie(1)) {
							System.out.println("            At least I got a cookie.");
						} else {
							System.out.println("            No food for me today.");
						}
					}

					// Finish the rest of the day
					try {
						Thread.sleep(timeAvailable);
					} catch (InterruptedException e) {
					}

					return null;
				}
			});

		}

		// Start the users of the vending machine
		final ExecutorService service = Executors.newFixedThreadPool(3);
		service.invokeAll(tasks);

		System.out.println("Cookies at end: " + vendingMachine.getCookiesAvailable());
		System.out.println("CandyBars at end: " + vendingMachine.getCandyBarsAvailable());

		System.out.println("Usage: " + vendingMachine.getUsageCount());

		// Shut down the vending machine and terminate the program
		vendingMachine.stopVendingMachine();
		service.shutdown();

		System.exit(0);
	}
}
